﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Chapter26
{
    using System.IO;
    using System.Diagnostics;
    using System.Threading;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Runtime.Remoting.Messaging;

    static partial class Program
    {
        internal static void Begin(Action action, bool active = false)
        {
            if (active)
            {
                Console.WriteLine("开始演示：");
                action();
                Console.WriteLine("演示完毕：");
                Console.ReadLine();
            }
        }

        internal static void C02()
        {
            Console.WriteLine("Main thread: queuing an asynchronous operation");
            ThreadPool.QueueUserWorkItem(CallBack, 5);
            Console.WriteLine("Main thread: doing other program...");
            Console.ReadLine();
        }

        private static void CallBack(object state)
        {
            Console.WriteLine("In ComputeBoundOp: state={0}", state);
            Thread.Sleep(1000);
        }

        internal static void C03()
        {
            CallContext.LogicalSetData("Name", "Jeffrey");
            ThreadPool.QueueUserWorkItem(state => Console.WriteLine("Name={0}", CallContext.LogicalGetData("Name")));
            ExecutionContext.SuppressFlow();
            ThreadPool.QueueUserWorkItem(state => Console.WriteLine("Name={0}", CallContext.LogicalGetData("Name")));
        }

        private static void C04()
        {
            Go();
            CtsLinkCts();
        }

        private static void Go()
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            ThreadPool.QueueUserWorkItem(o => Count(cts.Token, 1000));
            Console.WriteLine("Press <Enter> to cancel the operation.");
            Console.ReadLine();
            cts.Cancel();
        }

        private static void Count(CancellationToken token, int countTo)
        {
            for (int i = 0; i < countTo; i++)
            {
                if (token.IsCancellationRequested)
                {
                    Console.WriteLine("Count is cancelled");
                    break;
                }
                Console.WriteLine(i);
                Thread.Sleep(200);
            }
            Console.WriteLine("Count is done");
        }

        private static void RegisterTest()
        {
            var cts = new CancellationTokenSource();
            cts.Token.Register(() => Console.WriteLine("Canceled 1"));
            cts.Token.Register(() => Console.WriteLine("Canceled 1"));
            Thread.Sleep(200);
            cts.Cancel();
        }

        private static void CtsLinkCts()
        {
            var cts1 = new CancellationTokenSource();
            cts1.Token.Register(() => Console.WriteLine("Cts1 canceled"));
            var cts2 = new CancellationTokenSource();
            cts2.Token.Register(() => Console.WriteLine("Cts2 canceled"));
            var linkedCts = new CancellationTokenSource();
            linkedCts.Token.Register(() => Console.WriteLine("linkedCts canceled"));
            cts2.Cancel();
            Console.WriteLine("Cts1 canceled={0}, cts2 canceled={1}, linkedCts={2}",
                cts1.IsCancellationRequested, cts2.IsCancellationRequested, linkedCts.IsCancellationRequested);
        }

        internal static void C05()
        {
            try
            {
                var cts = new CancellationTokenSource();
                var t = new Task<int>(n => Sum(cts.Token, 10000), cts.Token);
                t.Start();
                cts.Cancel();
                Console.WriteLine("<C05>The Sum is: " + t.Result);
            }
            catch (AggregateException e)
            {
                e.Handle(pre => pre is OperationCanceledException);
                Console.WriteLine("Sum was canceled");
            }
            ContinueTask();
            Options();
            ParentChild();
            Factory();
        }

        private static int Sum(int n)
        {
            int sum = 0;
            for (; n > 0; n--)
            {
                checked { sum += n; }
            }
            return sum;
        }

        private static int Sum(CancellationToken ct, int n)
        {
            int sum = 0;
            for (; n > 0; n--)
            {
                ct.ThrowIfCancellationRequested();
                checked { sum += n; }
            }
            return sum;
        }

        private static void ContinueTask()
        {
            var t = new Task<int>(n => Sum((int)n), 7000);
            t.Start();
            var cwt = t.ContinueWith(task => Console.WriteLine("<ContinueTask>The sum is: " + task.Result));
        }

        private static void Options()
        {
            var t = new Task<int>(n => Sum((int)n), 8000);
            t.Start();
            t.ContinueWith(task => Console.WriteLine("<Options>The sum is: " + task.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
            t.ContinueWith(task => Console.WriteLine("The threw: " + task.Exception), TaskContinuationOptions.OnlyOnFaulted);
            t.ContinueWith(task => Console.WriteLine("The sum canceled: " + task.Result), TaskContinuationOptions.OnlyOnCanceled);
        }

        private static void ParentChild()
        {
            var parent = new Task<int[]>(() =>
                {
                    var results = new int[3];
                    new Task((n1) => results[0] = Sum(1234), TaskContinuationOptions.AttachedToParent).Start();
                    new Task((n2) => results[1] = Sum(2345), TaskContinuationOptions.AttachedToParent).Start();
                    new Task((n3) => results[2] = Sum(3456), TaskContinuationOptions.AttachedToParent).Start();
                    return results;
                });
            var cwt = parent.ContinueWith(parentTask => Array.ForEach(parentTask.Result, Console.WriteLine));
            parent.Start();
        }

        private static void Factory()
        {
            var parent = new Task(() =>
            {
                var cts = new CancellationTokenSource();
                var tf = new TaskFactory<int>(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
                var childTasks = new[]
                    {
                        tf.StartNew(()=>Sum(cts.Token, 10000)),
                        tf.StartNew(()=>Sum(cts.Token, 20000)),
                        tf.StartNew(()=>Sum(cts.Token, int.MaxValue))
                    };
                for (int i = 0; i < childTasks.Length; i++)
                {
                    childTasks[i].ContinueWith(t => cts.Cancel(), TaskContinuationOptions.OnlyOnFaulted);
                }
                tf.ContinueWhenAll(
                    childTasks,
                    completedTasks => completedTasks.Where(t => !t.IsFaulted && !t.IsCanceled).
                        Max(t => t.Result),
                        CancellationToken.None).ContinueWith(t => Console.WriteLine("The maximun is: " + t.Result), TaskContinuationOptions.ExecuteSynchronously);
            });
            parent.ContinueWith(p =>
                {
                    var sb = new StringBuilder("The following execption(s) occurred: " + Environment.NewLine);
                    foreach (var e in p.Exception.Flatten().InnerExceptions)
                    {
                        sb.AppendLine(" " + e.GetType().ToString());
                        Console.WriteLine(sb.ToString());
                    }
                }, TaskContinuationOptions.OnlyOnFaulted);
            parent.Start();
        }

        internal static void C06()
        {
            Console.WriteLine(DirectoryBytes(@"D:\MyJobs", "*.*", SearchOption.AllDirectories));
        }

        private static long DirectoryBytes(string path, string searchPattern, SearchOption searchOption)
        {
            var files = Directory.EnumerateFiles(path, searchPattern, searchOption);
            long masterTotal = 0;
            var result = Parallel.ForEach<string, long>(
                files,
                () =>
                {
                    return 0;
                },
                (file, loopState, index, taskLocalTotal) =>
                {
                    long fileLength = 0;
                    FileStream fs = null;
                    try
                    {
                        fs = File.OpenRead(file);
                        fileLength = fs.Length;
                    }
                    catch (IOException) { }
                    finally
                    {
                        if (fs != null)
                            fs.Close();
                    }
                    return taskLocalTotal + fileLength;
                },
                taskLocalTotal =>
                {
                    Interlocked.Add(ref masterTotal, taskLocalTotal);
                });
            return masterTotal;
        }

        private static void ObsoleteMethods(Assembly assembly)
        {
            var query = from type in assembly.GetExportedTypes().AsParallel()
                        from method in type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
                        let obsoleteAttrType = typeof(ObsoleteAttribute)
                        where Attribute.IsDefined(method, obsoleteAttrType)
                        orderby type.FullName
                        let obsoleteAttrObj = (ObsoleteAttribute)Attribute.GetCustomAttribute(method, obsoleteAttrType)
                        select string.Format("Type={0}\nMethod={1}\nMessage={2}\n", type.FullName, method.ToString(), obsoleteAttrType.MemberType);
            foreach (var item in query)
            {
                Console.WriteLine(item);
            }
        }

        internal static void C07()
        {
            ObsoleteMethods(Assembly.GetAssembly(typeof(int)));
        }

        private static class TimerDemo
        {
            private static Timer timer;

            internal static void Go()
            {
                Console.WriteLine("Main thread: starting a tiemr");
                using (timer = new Timer(CallBack, 6, 0, Timeout.Infinite))
                {
                    Thread.Sleep(10000);
                }
            }

            private static void Callback(object state)
            {
                Console.WriteLine("In ComputeBoundOp: state={0}", state);
                Thread.Sleep(1000);
                timer.Change(2000, Timeout.Infinite);
            }
        }

        internal static void C08()
        {
            TimerDemo.Go();
        }

        private static class CacheLine
        {
            [StructLayout(LayoutKind.Explicit)]
            private class Data
            {
                [FieldOffset(0)]
                public int field1;
                [FieldOffset(64)]
                public int field2;
            }

            private const int iterations = 100000000;
            private static int operations = 2;
            private static long startTime;

            public static void Go()
            {
                var data = new Data();
                startTime = Stopwatch.GetTimestamp();
                ThreadPool.QueueUserWorkItem(o => AccessData(data, 0));
                ThreadPool.QueueUserWorkItem(o => AccessData(data, 1));
                Console.ReadLine();
            }

            private static void AccessData(Data data, int field)
            {
                for (int i = 0; i < iterations; i++)
                {
                    if (field == 0)
                        data.field1++;
                    else
                        data.field2++;
                }
                if (Interlocked.Decrement(ref operations) == 0)
                    Console.WriteLine("Access time: {0:N0}", (Stopwatch.GetTimestamp() - startTime) / (Stopwatch.Frequency / 1000));
            }
        }

        internal static void C10()
        {
            CacheLine.Go();
        }
    }
}